package site.luojie.core.factory;

import site.luojie.core.annotation.AnnotationUtil;
import site.luojie.core.annotation.Autowired;
import site.luojie.core.annotation.ComponentScan;
import site.luojie.core.bean.Bean;
import site.luojie.core.bean.BeanProperty;
import site.luojie.core.bean.BeanPropertyType;
import site.luojie.core.filter.ClassAnnotationFilter;
import site.luojie.utils.ClassUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

import static site.luojie.core.annotation.AnnotationUtil.classContainTransactional;
import static site.luojie.utils.ClassFieldUtil.fieldConstantsAnnotation;
import static site.luojie.utils.StringUtil.toLowerCaseFirstChar;

/**
 * @Description: 根据注解加载
 * @Author jie.luo
 * @Create: 2020-05-05 22:57
 **/
public class AnnotationApplicationContext extends AbstractApplicationContext {

    private Map<Class<?>, Object> beanClassMap = new HashMap<>();

    public AnnotationApplicationContext(Class<?> clazz) throws Exception {
        // 解析注解、扫描包
        parseConfigClass(clazz);
        createBean();
    }

    /**
     * 解析主配置类
     *
     * @param clazz 主配置类
     */
    public void parseConfigClass(Class<?> clazz) {
        Set<Class<?>> contextClasses = new HashSet<>();
        Annotation[] configAnnotations = clazz.getAnnotations();
        for (Annotation configAnnotation : configAnnotations) {
            if (configAnnotation.annotationType() == ComponentScan.class) {
                ComponentScan componentScan = (ComponentScan) configAnnotation;

                String packageName = componentScan.value();
                // 扫描包路径 所有类
                Set<Class<?>> allClass = ClassUtil.getClassSet(packageName);

                // 有注解 Component、Repository、Service 的类
                contextClasses = ClassAnnotationFilter.filter(allClass);
            }
        }

        // 解析注解为 List<Bean>
        parseContextClasses(contextClasses);
    }

    /**
     * 解析注解为 List<Bean>
     */
    public void parseContextClasses(Set<Class<?>> contextClasses) {
        Bean bean = null;
        for (Class<?> contextClass : contextClasses) {
            bean = new Bean();

            // 获取容器名
            String name = AnnotationUtil.getName(contextClass);
            bean.setId(name);
            bean.setClassName(contextClass.getName());
            bean.setClasses(contextClass);
            // 获取类型所有属性
            Field[] fields = contextClass.getDeclaredFields();
            for (Field field : fields) {
                boolean constantsAnnotation = fieldConstantsAnnotation(field, Autowired.class);
                if (constantsAnnotation) {
                    BeanProperty beanProperty = new BeanProperty();
                    beanProperty.setName(field.getName());
                    beanProperty.setParentId(bean.getId());
                    beanProperty.setType(BeanPropertyType.TYPE);
                    beanProperty.setClassType(field.getType());

                    bean.getBeanProperties().add(beanProperty);
                }
            }
            beans.add(bean);
        }
    }

    /**
     * 创建 Bean
     */
    private void createBean() throws Exception {
        // 所有Bean 下的 property
        List<BeanProperty> beanProperties = new ArrayList<>();

        // 容器创建bean
        for (Bean bean : beans) {
            Class<?>[] interfaces = bean.getClasses().getInterfaces();

            Object o = Class.forName(bean.getClassName()).newInstance();

            for (Class<?> anInterface : interfaces) {
                String interfaceSimpleName = toLowerCaseFirstChar(anInterface.getSimpleName());
                beanMap.put(interfaceSimpleName, o);
                beanClassMap.put(anInterface, o);
            }

            beanMap.put(bean.getId(), o);
            beanClassMap.put(o.getClass(), o);
            beanProperties.addAll(bean.getBeanProperties());
        }

        // bean di
        if (beanProperties.size() > 0) {
            for (BeanProperty beanProperty : beanProperties) {

                String parentId = beanProperty.getParentId();
                Object parentObject = beanMap.get(parentId);

                String ref = beanProperty.getRef();
                Object refObject = null;
                if (null == ref) {
                    refObject = beanMap.get(toLowerCaseFirstChar(beanProperty.getClassType().getSimpleName()));
                } else {
                    refObject = beanMap.get(ref);
                }

                Field[] declaredFields = parentObject.getClass().getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.getName().equals(beanProperty.getName())) {
                        declaredField.setAccessible(true);
                        declaredField.set(parentObject, refObject);
                    }
                }
                beanClassMap.put(parentObject.getClass(), parentObject);
                beanMap.put(parentId, parentObject);
            }
        }

        // 检测构建的bean是否需要通过代理进行代理
        ProxyFactory proxyFactory = this.getBean(ProxyFactory.class);
        for (Bean bean : beans) {
            // 判断该类是否开启事务
            boolean classContainTransactional = classContainTransactional(bean.getClasses());
            if (classContainTransactional) {
                Object object = beanMap.get(bean.getId());
                Object proxy = proxyFactory.getProxy(object);

                for (Class<?> anInterface : object.getClass().getInterfaces()) {
                    String interfaceSimpleName = toLowerCaseFirstChar(anInterface.getSimpleName());
                    beanMap.put(interfaceSimpleName, proxy);
                    beanClassMap.put(anInterface, proxy);
                }

                beanMap.put(bean.getId(), proxy);
                beanClassMap.put(object.getClass(), proxy);
            }

        }
    }

    @Override
    public Object getContextBean(String name) {
        return beanMap.get(name);
    }

    @Override
    public <T> T getContextBean(Class<T> requiredType) {
        return (T) beanClassMap.get(requiredType);
    }

}
